﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ComputerBeacon.Csv {
    /// <summary>
    /// A table containing string values taken from a csv string
    /// </summary>
    public class CsvTable {
        /// <summary>
        /// Gets the headers of the table, if present
        /// </summary>
        public string[] Headers { get; private set; }

        /// <summary>
        /// Gets the data rows of the table
        /// </summary>
        public ReadOnlyCollection<string[]> Rows { get; private set; }

        /// <summary>
        /// Parses a csv string into a table
        /// </summary>
        /// <param name="csvString">Csv string</param>
        /// <param name="headerLine">Whether the data includes a header line</param>
        public CsvTable(string csvString, bool headerLine = false) {
            var scanner = new Scanner(csvString);

            var firstrow = new List<string>();
            var rows = new List<string[]>();
            Scanner.Token token;

            do {
                token = scanner.Next();
                firstrow.Add(token.Value);
            } while (token.NextType == Scanner.TokenType.String);

            if (headerLine) Headers = firstrow.ToArray();
            else rows.Add(firstrow.ToArray());
            this.ColumnsCount = firstrow.Count;

            while (token.NextType != Scanner.TokenType.End) {
                var row = new string[ColumnsCount];
                for (int i = 0; i < this.ColumnsCount; i++) {
                    token = scanner.Next();

                    if (i == 0 && token.NextType == Scanner.TokenType.End) goto exit;
                    if ((i + 1 == this.ColumnsCount) == (token.NextType == Scanner.TokenType.String)) throw new FormatException("Inconsistent column count in tabular data");
                    row[i] = token.Value;
                }
                rows.Add(row);
            }

            exit:
            this.Rows = new ReadOnlyCollection<string[]>(rows);
        }
        /// <summary>
        /// Gets the data of a particualr column of a specific row
        /// </summary>
        /// <param name="row"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public string this[int row, string columnName] {
            get {
                if (this.Headers == null) throw new InvalidOperationException("No header information is specified");
                int columnIndex = -1;
                for (int i = 0; i < this.ColumnsCount; i++) {
                    if (this.Headers[i] == columnName) {
                        columnIndex = i;
                        break;
                    }
                }

                if (columnIndex == -1) throw new KeyNotFoundException(string.Format("No column with name \"{0}\" can be found", columnName));
                return this.Rows[row][columnIndex];
            }
        }

        /// <summary>
        /// Gets the data at a specific cell
        /// </summary>
        /// <param name="row"></param>
        /// <param name="column"></param>
        /// <returns></returns>
        public string this[int row, int column] {
            get {
                return this.Rows[row][column];
            }
        }


        /// <summary>
        /// Gets the total number of columns
        /// </summary>
        public int ColumnsCount { get; private set; }
    }
}
